package com.flow.framework.schedule.config;

import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.exception.CheckedException;
import com.flow.framework.common.util.verify.VerifyUtil;
import com.flow.framework.core.constant.FrameworkCoreConstant;
import com.flow.framework.core.service.properties.ISystemConfigPropertiesService;
import com.flow.framework.core.system.thread.pool.executor.SystemStatusCheckerExecutor;
import com.flow.framework.schedule.config.condition.RpcScheduleJobExecutorInitCondition;
import com.flow.framework.schedule.executor.RpcScheduleJobExecutor;
import com.flow.framework.schedule.properties.FrameworkScheduleConfigProperties;
import com.flow.framework.schedule.properties.rpc.RpcConfigProperties;
import com.flow.framework.schedule.service.system.health.impl.ScheduleJobHealthCheckServiceImpl;
import com.flow.framework.schedule.system.checker.impl.RemoteScheduleNetworkChecker;
import com.flow.framework.schedule.system.listener.lifecycle.LocalScheduleLifecycleListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

/**
 * 框架调度配置
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/4/9
 */
@Slf4j
@Configuration
public class FrameworkScheduleConfig {

    @Bean
    @ConditionalOnMissingBean
    @RefreshScope
    @ConfigurationProperties(prefix = "customization.framework.schedule")
    FrameworkScheduleConfigProperties frameworkScheduleConfigProperties() {
        return new FrameworkScheduleConfigProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    ScheduleJobHealthCheckServiceImpl scheduleJobHealthCheckService() {
        return new ScheduleJobHealthCheckServiceImpl();
    }

    @Bean
    @ConditionalOnMissingBean
    LocalScheduleLifecycleListener localScheduleLifecycleListener(FrameworkScheduleConfigProperties frameworkScheduleConfigProperties) {
        return new LocalScheduleLifecycleListener(frameworkScheduleConfigProperties);
    }

    @Bean
    @Conditional(RpcScheduleJobExecutorInitCondition.class)
    RpcScheduleJobExecutor rpcScheduleJobExecutor(ISystemConfigPropertiesService systemConfigPropertiesService,
                                                  FrameworkScheduleConfigProperties frameworkScheduleConfigProperties) {
        RpcConfigProperties rpcConfigProperties = frameworkScheduleConfigProperties.getRpc();
        RpcScheduleJobExecutor rpcScheduleJobExecutor = new RpcScheduleJobExecutor();
        rpcScheduleJobExecutor.setAdminAddresses(rpcConfigProperties.getAdminAddresses());
        rpcScheduleJobExecutor.setAppName(systemConfigPropertiesService.getConfigValue(FrameworkCoreConstant.SERVICE_NAME_KEY));
        String clientIp = rpcConfigProperties.getClientIp();
        if (VerifyUtil.isEmpty(clientIp)) {
            rpcScheduleJobExecutor.setIp(systemConfigPropertiesService.getConfigValue(FrameworkCoreConstant.SERVICE_CLIENT_IP_KEY, null));
        }
        if (VerifyUtil.isEmpty(clientIp)) {
            clientIp = systemConfigPropertiesService.getConfigValue(FrameworkCoreConstant.SERVICE_BIND_IP_KEY, null);
        }
        if (VerifyUtil.isEmpty(clientIp)) {
            clientIp = randomLocalIp();
        }

        rpcScheduleJobExecutor.setIp(clientIp);
        rpcScheduleJobExecutor.setPort(rpcConfigProperties.getClientPort());
        rpcScheduleJobExecutor.setAccessToken(rpcConfigProperties.getAccessToken());
        String clientLogPath = rpcConfigProperties.getClientLogPath();
        if (VerifyUtil.isEmpty(clientLogPath)) {
            clientLogPath = System.getProperty("user.home") + File.pathSeparator + "schedule/client/logs";
        }
        rpcScheduleJobExecutor.setLogPath(clientLogPath);
        rpcScheduleJobExecutor.setLogRetentionDays(rpcConfigProperties.getClientLogRetentionDays());
        SystemStatusCheckerExecutor.schedule(new RemoteScheduleNetworkChecker(rpcConfigProperties));
        return rpcScheduleJobExecutor;
    }

    private String randomLocalIp() {
        try {
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = networkInterfaces.nextElement();
                if (!networkInterface.isUp()) {
                    continue;
                }
                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress inetAddress = addresses.nextElement();
                    String hostAddress = inetAddress.getHostAddress();
                    if (!"127.0.0.1".equals(hostAddress)) {
                        return hostAddress;
                    }
                }
            }
            log.error("can't find local ip.");
            throw new CheckedException(SystemErrorCode.SYSTEM_START_ERROR);
        } catch (SocketException e) {
            log.error("random local ip error.", e);
            throw new CheckedException(SystemErrorCode.SYSTEM_START_ERROR, "random local ip error.", e);
        }
    }
}
